package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.job.IotMsgNotifyData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.DeviceReportedVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * deviceService业务层处理
 *
 * @author LiuLi
 * @date 2025-08-21
 */
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询device
     *
     * @param id device主键
     * @return device
     */
    @Override
    public Device selectDeviceById(Integer id) {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询device列表
     *
     * @param device device
     * @return device
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增device
     *
     * @param device device
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return deviceMapper.insert(device);
    }

    /**
     * 修改device
     *
     * @param device device
     * @return 结果
     */
    @Override
    public int updateDevice(Device device) {
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除device
     *
     * @param ids 需要删除的device主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Integer[] ids) {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除device信息
     *
     * @param id device主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Integer id) {
        return deviceMapper.deleteById(id);
    }


    /**
     * 同步产品列表
     */
    @Override
    public void syncProductList() {
        // 1. 调用Iot服务获取产品列表
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        ListProductsResponse response = client.listProducts(listProductsRequest);
        //2. 处理响应
        if (response.getHttpStatusCode() == 200) {
            List<ProductSummary> products = response.getProducts();
            // 3. 写入Redis,以Json 格式保存
            redisTemplate.opsForValue().set(Constants.CACHE_IOT_ALL_PRODUCT, JSONUtil.toJsonStr(products));
        } else {
            System.out.println("查询产品列表失败，状态码：" + response.getHttpStatusCode());
        }
    }

    /**
     * 查询所有产品
     *
     * @return 产品列表
     */
    @Override
    public List<ProductVo> listAllProduct() {
        //1. 从Redis中获取产品列表
        String json = (String) redisTemplate.opsForValue().get(Constants.CACHE_IOT_ALL_PRODUCT);
        //2. 如果Redis中没有数据，则返回一个空列表
        if (StringUtils.isBlank(json)) {
            log.info("从Redis中获取产品列表失败");
            return new ArrayList<>();
        }
        //3. 将得到的Json转换为产品列表, 并返回
        return JSONUtil.toList(json, ProductVo.class);
    }

    /**
     * 注册设备
     *
     * @param deviceDto
     * @return
     */
    @Override
    public int registerDevice(DeviceDto deviceDto) {
        //1. 判断设备名称是否重复
        checkDeviceName(deviceDto.getDeviceName());
        //2. 判断设备ID是否重复
        checkNodeId(deviceDto.getNodeId());
        //3. 判断同一个位置是否重复绑定设备
        checkBindingLocation(deviceDto.getBindingLocation(), deviceDto.getLocationType(), deviceDto.getPhysicalLocationType(), deviceDto.getProductKey());
        //4.保存设备到IOT
        if (deviceDto.getLocationType() == 0) {
            // 如果是随身设备, 则设置物理位置类型为-1
            deviceDto.setPhysicalLocationType(-1);
        }
        AddDeviceResponse response = saveDeviceToIot(deviceDto);
        //5.保存设备到数据库
        saveDevice(deviceDto, response.getDeviceId(), response.getAuthInfo().getSecret());
        return 1;

    }

    /**
     * 根据物联网设备ID查询设备详情
     *
     * @param iotId 物联网设备ID
     * @return 设备详情
     */
    @Override
    public DeviceDetailVo getByIotId(String iotId) {
        //1. 根据ID从华为云IOT平台查询设备
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("查询设备失败");
        }
        String activeTime = response.getActiveTime();
        String status = response.getStatus();

        // 处理时间转换：UTC时间 -> 中国时区LocalDateTime
        LocalDateTime chinaActiveTime = null;
        if (StringUtils.isNotBlank(activeTime)) {
            // 定义UTC时间格式器
            DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
            // 解析为UTC时区的ZonedDateTime
            ZonedDateTime utcTime = ZonedDateTime.parse(activeTime, utcFormatter.withZone(ZoneId.of("UTC")));
            // 转换为中国时区（Asia/Shanghai）的LocalDateTime
            chinaActiveTime = utcTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
        }

        //2.从本地数据库查询设备
        Device device = this.lambdaQuery().eq(Device::getIotId, iotId).one();
        if (device == null) {
            throw new BaseException("设备不存在");
        }

        //3. 转换为VO对象
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        deviceDetailVo.setActiveTime(chinaActiveTime);
        deviceDetailVo.setDeviceStatus(status);
        return deviceDetailVo;
    }

    /**
     * 查询设备上报的属性
     *
     * @param iotId 物联网设备id
     * @return 设备服务属性
     */
    @Override
    public List<DeviceReportedVo> queryServiceProperties(String iotId) {
        // 1. 从IOT平台查询设备上报的属性
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("查询设备失败");
        }
        List<DeviceShadowData> shadow = response.getShadow();
        if (shadow.isEmpty()) {
            throw new BaseException("设备上报数据为空");
        }

        // 2. 解析上报数据并处理时区转换
        List<DeviceReportedVo> deviceReportedVoList = new ArrayList<>();
        // 定义UTC时间格式器（兼容带Z的UTC时间）
        DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX");

        shadow.forEach(data -> {
            String eventTime = data.getReported().getEventTime();
            // 先解析为UTC时间，再转换为北京时间
            LocalDateTime localDateTime = ZonedDateTime
                    .parse(eventTime, utcFormatter)  // 自动识别'Z'为UTC时区
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))  // 转换为北京时区
                    .toLocalDateTime();  // 转为LocalDateTime

            Map<String, Object> properties = (Map<String, Object>) data.getReported().getProperties();
            properties.forEach((key, value) -> {
                DeviceReportedVo deviceReportedVo = new DeviceReportedVo();
                deviceReportedVo.setFunctionId(key);
                deviceReportedVo.setValue(value);
                deviceReportedVo.setEventTime(localDateTime);
                deviceReportedVoList.add(deviceReportedVo);
            });
        });
        return deviceReportedVoList;
    }


    /**
     * 保存设备到数据库
     *
     * @param deviceDto 设备DTO
     * @param deviceId  物联网设备ID
     * @param secret    物联网设备密钥
     */
    private void saveDevice(DeviceDto deviceDto, String deviceId, String secret) {
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(deviceId);
        device.setSecret(secret);
        if (deviceDto.getPhysicalLocationType() == null) {
            device.setHaveEntranceGuard(0);
        } else {
            device.setHaveEntranceGuard(1);
        }
        try {
            save(device);
        } catch (Exception e) {
            throw new BaseException("保存设备到数据库失败了");
        }
    }

    /**
     * 保存设备到IOT
     *
     * @param deviceDto 设备DTO
     * @return 物联网设备响应
     */
    private AddDeviceResponse saveDeviceToIot(DeviceDto deviceDto) {
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice addDevice = new AddDevice();
        AuthInfo authInfo = new AuthInfo();
        authInfo.withAuthType("SECRET").withSecret(RandomUtil.randomString(32)).withSecureAccess(true);
        //IOT平台会在增加设备后，自动生成设备ID
        addDevice.withNodeId(deviceDto.getNodeId()).withDeviceName(deviceDto.getDeviceName()).withProductId(deviceDto.getProductKey()).withDescription(deviceDto.getDeviceDescription()).withAuthInfo(authInfo);
        request.withBody(addDevice);
        AddDeviceResponse response = client.addDevice(request);
        if (response.getHttpStatusCode() != 201) {
            throw new BaseException("设备注册失败");
        }
        return response;
    }

    /**
     * 判断同一个位置是否重复绑定设备
     *
     * @param bindingLocation      位置ID
     * @param locationType         位置类型
     * @param physicalLocationType 物理位置类型
     * @param productKey           产品ID
     */
    private void checkBindingLocation(Long bindingLocation, Integer locationType, Integer physicalLocationType, String productKey) {
        Long count = this.lambdaQuery().eq(Device::getBindingLocation, bindingLocation).eq(Device::getLocationType, locationType).eq(Device::getPhysicalLocationType, physicalLocationType).eq(Device::getProductKey, productKey).count();
        if (count > 0) {
            throw new BaseException("该位置已存在设备");
        }
    }

    /**
     * 判断设备名称是否重复
     *
     * @param deviceName 设备名称
     */
    private void checkDeviceName(String deviceName) {
        Long count = this.lambdaQuery().eq(Device::getDeviceName, deviceName).count();
        if (count > 0) {
            throw new BaseException("名称为" + deviceName + "的设备已存在");
        }
    }

    /**
     * 判断设备ID是否重复
     *
     * @param nodeId 设备ID
     */
    private void checkNodeId(String nodeId) {
        Long count = this.lambdaQuery().eq(Device::getNodeId, nodeId).count();
        if (count > 0) {
            throw new BaseException("ID为" + nodeId + "的设备已存在");
        }
    }
}
